package com.kasa.zto;

import java.io.UnsupportedEncodingException;
import java.sql.Time;
import java.util.Date;

/**
 * <p>Title: 编码器</p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2008</p>
 *
 * <p>Company: </p>
 *
 * @author gary
 * @version 1.0
 */
public abstract class ZTOEncoder
{
    public ZTOEncoder()
    {
    }
    
    public abstract byte[] getBytesData();
    
    protected void putByte(byte b)
    {
        putBytes(new byte[]{b});
    }
    
    protected abstract void putBytes(byte[] bs);
    
    private byte getDigit(long l)
    {
    	byte digit = 0;
        if (l >= 0x80000000000000L || l < -0x80000000000000L)
        {
            digit = 8;
        }
        else if (l >= 0x800000000000L || l < -0x800000000000L)
        {
            digit = 7;
        }
        else if (l >= 0x8000000000L || l < -0x8000000000L)
        {
            digit = 6;
        }
        else if (l >= 0x80000000L || l < -0x80000000L)
        {
            digit = 5;
        }
        else if (l >= 0x800000L || l < -0x800000L)
        {
            digit = 4;
        }
        else if (l >= 0x8000L || l < -0x8000L)
        {
            digit = 3;
        }
        else if (l >= 0x80L || l < -0x80L)
        {
            digit = 2;
        }
        else
        {
            digit = 1;
        }
        return digit;
    }
    
    public abstract void encode_long(long l, byte digit) throws ZTOException;
    
    /**
     * @param number 参数number只考虑Long型的数据
     * @throws ZTOException
     */
    private void encodeNumber(Number number) throws ZTOException
    {
    	if (null == number)
        {
            encode_byte(ZTOTag.NULL);
        }
        else
        {
        	long l = number.longValue();
        	byte digit = getDigit(l);
            encode_byte(l < 0 ? (byte)-digit : digit);
            encode_long(l, digit);
        }
    }
    
    public void encodeBoolean(Boolean b) throws ZTOException
    {
        if (null == b)
        {
            encode_byte((byte) 2);
        }
        else
        {
            encode_boolean(b);
        }
    }

    public void encodeByte(Byte b) throws ZTOException
    {
        if (null == b)
        {
            encode_byte(ZTOTag.NULL);
        }
        else
        {
            encode_byte((byte)1);
            encode_byte(b);
        }
    }
    
    public void encodeShort(Short s) throws ZTOException
    {
    	encodeNumber(s);
    }

    public void encodeInteger(Integer i) throws ZTOException
    {
    	encodeNumber(i);
    }

    public void encodeLong(Long l) throws ZTOException
    {
    	encodeNumber(l);
    }

    public void encodeFloat(Float f) throws ZTOException
    {
    	encodeNumber(null == f ? null : (long) (f * 1000));
    }

    public void encodeDouble(Double d) throws ZTOException
    {
    	encodeNumber(null == d ? null : (long) (d * 1000));
    }

    public void encode_boolean(boolean b) throws ZTOException
    {
        encode_byte(b ? (byte) 1 : 0);
    }

    public void encode_byte(byte b) throws ZTOException
    {
        putByte(b);
    }
    
    public void encode_bytes(byte[] bs) throws ZTOException
    {
        if (null == bs)
        {
            encode_int(ZTOTag.NULL);
        }
        else
        {
            int sLength = bs.length;
            encode_int(sLength);
            putBytes(bs);
        }
    }
    
    public void encode_short(short i) throws ZTOException
    {
        encode_long(i);
    }

    public void encode_int(int i) throws ZTOException
    {
        encode_long(i);
    }
    
    public void encode_long(long l) throws ZTOException
    {    	
    	byte digit = getDigit(l);
        encode_byte(l < 0 ? (byte)-digit : digit);
        encode_long(l, digit);
        
    	//不建议用下列方法decodeNumber()，否则会有基本数据类型与数字对象间的转换，影响速度
//    	encodeNumber(l);
    }

    public void encode_float(float f) throws ZTOException
    {
        long l = (long) (f * 1000);
        encode_long(l);
    }

    public void encode_double(double d) throws ZTOException
    {
        long l = (long) (d * 1000);
        encode_long(l);
    }
    
    public void encodeString(String s, String charsetName) throws ZTOException
    {
        if (null == s)
        {
            encode_int(ZTOTag.NULL);
        }
        else if(0 == s.length())
        {
            encode_int(-1);
        }
        else
        {
            try {
				byte[] sByte = s.getBytes(charsetName);
				int sLength = sByte.length;
				encode_int(sLength);
				putBytes(sByte);
			} catch (UnsupportedEncodingException e) {
				byte[] sByte = s.getBytes();
				int sLength = sByte.length;
				encode_int(sLength);
				putBytes(sByte);
			}
        }
    }

    public void encodeString(String s) throws ZTOException
    {
        if (null == s)
        {
            encode_int(ZTOTag.NULL);
        }
        else if(0 == s.length())
        {
            encode_int(-1);
        }
        else
        {
            byte[] sByte = s.getBytes();
            int sLength = sByte.length;
            encode_int(sLength);
            putBytes(sByte);
        }
    }
    
    public void encodeString(byte[] sByte, int bSize) throws ZTOException
    {
        byte[] putBytes = new byte[bSize];
        for (int i = 0; i < bSize; i++)
        {
            putBytes[i] = 32;
        }
        int newSize = sByte.length;
        System.arraycopy(sByte, 0, putBytes, 0, Math.min(bSize, newSize));
        putBytes(putBytes);
    }

    public void encodeDate(Date d) throws ZTOException
    {
        encodeLong(null == d ? null : d.getTime());
    }

    public void encodeTime(Time d) throws ZTOException
    {
        encodeLong(null == d ? null : d.getTime());
    }

    public void encodeZTOType(ZTOType s) throws ZTOException
    {
        if (null == s)
        {
            encode_byte(ZTOTag.NULL);
        }
        else
        {
            encode_byte(ZTOTag.ZTOType);
            s.encode(this);
        }
    }

    public void encodeZTOAny(ZTOAny s) throws ZTOException
    {
        if (null == s)
        {
            encode_byte(ZTOTag.NULL);
        }
        else
        {
            encode_byte(ZTOTag.ZTOAny);
            s.encode(this);
        }
    }

    public void encodeZTOArrayList(ZTOArrayList<? extends ZTOType> list) throws ZTOException
    {
        if (null == list)
        {
            encode_byte(ZTOTag.NULL);
        }
        else
        {
            encode_byte(ZTOTag.ZTOArrayList);
            list.encode(this);
        }
    }

    public void encodeZTOLinkedHashMap(ZTOLinkedHashMap<? extends ZTOType, ? extends ZTOType> map) throws ZTOException
    {
        if (null == map)
        {
            encode_byte(ZTOTag.NULL);
        }
        else
        {
            encode_byte(ZTOTag.ZTOLinkedHashMap);
            map.encode(this);
        }
    }
}
